package flc.ast.activity;

import androidx.appcompat.app.AppCompatActivity;

import android.Manifest;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;

import com.blankj.utilcode.util.FileUtils;
import com.blankj.utilcode.util.ThreadUtils;
import com.blankj.utilcode.util.TimeUtils;
import com.blankj.utilcode.util.ToastUtils;
import com.stark.media.recorder.AudioRecorder;
import com.stark.media.recorder.IRecStateListener;
import com.stark.media.recorder.RecState;
import com.stark.midi.lib.mid.MidiFile;
import com.stark.midi.lib.mid.event.MidiEvent;
import com.stark.midi.lib.mid.event.NoteOff;
import com.stark.midi.lib.mid.event.NoteOn;
import com.stark.midi.lib.mid.util.MidiEventListener;
import com.stark.midi.lib.mid.util.MidiProcessor;
import com.stark.midi.lib.model.MidFileBean;
import com.stark.piano.lib.widget.PianoConst;
import com.stark.piano.lib.widget.PianoKeyBoard;
import com.stark.piano.lib.widget.PianoSeekBar;
import com.stark.piano.lib.widget.PianoSongPlayer;

import java.io.File;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

import flc.ast.BaseAc;
import flc.ast.R;
import flc.ast.bean.PracticeTime;
import flc.ast.databinding.ActivityPianoTrainBinding;
import flc.ast.dialog.RecordDialog;
import flc.ast.utils.AppSpUtils;
import flc.ast.utils.Content;
import flc.ast.utils.PracticeDatabase;
import io.reactivex.rxjava3.core.ObservableEmitter;
import stark.common.basic.utils.AssetUtil;
import stark.common.basic.utils.RxUtil;
import stark.common.basic.utils.StkPermissionHelper;
import stark.common.basic.utils.TimeUtil;

public class PianoTrainActivity extends BaseAc<ActivityPianoTrainBinding> implements IRecStateListener {

    public static PianoSongPlayer.Mode pianoTrainMode = PianoSongPlayer.Mode.APPRECIATE;
    public static MidFileBean playFileBean;
    private MidiProcessor mProcessor;
    private int mNoteEventCount;
    private AudioRecorder mRecorder;
    private long today, startTime, endTime;
    private Handler mHandler;
    private final Runnable mTaskUpdateTime = new Runnable() {
        @Override
        public void run() {
            long duration = mRecorder.getRecDuration();
            String strTime = TimeUtil.getMmss(duration);
            mDataBinding.tvPianoTrainTime.setText(strTime);
            mHandler.postDelayed(this, 1000);
        }
    };

    @Override
    protected int onCreate() {
        return R.layout.activity_piano_train;
    }

    @Override
    protected void initView() {
        mHandler = new Handler();
        mDataBinding.ivPianoTrainBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onBackPressed();
            }
        });
        mDataBinding.ivPianoRecordList.setOnClickListener(this);
        mDataBinding.llPianoRecord.setOnClickListener(this);
        mDataBinding.ivPianoTrainMode.setOnClickListener(this);
        mDataBinding.ivPianoTrainAdd.setOnClickListener(this);
        mDataBinding.ivPianoTrainReduce.setOnClickListener(this);
        if (pianoTrainMode == PianoSongPlayer.Mode.APPRECIATE) {
            mDataBinding.ivPianoTrainMode.setImageResource(R.drawable.lx1);
            mDataBinding.ivPianoRecordList.setVisibility(View.GONE);
            mDataBinding.llPianoRecord.setVisibility(View.GONE);
        } else {
            mDataBinding.ivPianoTrainMode.setImageResource(R.drawable.xs1);
            mDataBinding.ivPianoRecordList.setVisibility(View.VISIBLE);
            mDataBinding.llPianoRecord.setVisibility(View.VISIBLE);
        }
        mDataBinding.pianoSeekBar.setProgressChangeListener(new PianoSeekBar.IProgressChangeListener() {
            @Override
            public void onProgressChanged(float percent) {
                mDataBinding.pianoSongPlayer.getPianoKeyBoard().move(percent);
            }
        });
        mDataBinding.pianoSongPlayer.setMode(pianoTrainMode);
        mDataBinding.pianoSongPlayer.setReleaseSoundWhenDetachWindow(false);
        mDataBinding.pianoSongPlayer.setPianoMoveListener(percent -> mDataBinding.pianoSeekBar.moveTo(percent));
        mDataBinding.pianoSongPlayer.setAddPianoListener(new PianoSongPlayer.IAddPianoListener() {
            @Override
            public void onAddPiano(PianoKeyBoard pianoKeyBoard) {
                mDataBinding.pianoSeekBar.setRatioKeyCountInScreen(pianoKeyBoard.getRatioKeyCountInScreen());
                pianoKeyBoard.setBlackKeyDrawable((BitmapDrawable) getResources().getDrawable(R.drawable.kk3));
                pianoKeyBoard.setBlackKeyPressedDrawable((BitmapDrawable) getResources().getDrawable(R.drawable.kk4));
                pianoKeyBoard.setWhiteKeyDrawable((BitmapDrawable) getResources().getDrawable(R.drawable.kk1));
                pianoKeyBoard.setWhiteKeyPressedDrawable((BitmapDrawable) getResources().getDrawable(R.drawable.kk2));
                pianoKeyBoard.setPronuncTextColor(Color.parseColor("#80292929"));
                pianoKeyBoard.setPronuncTextDimension(30);
            }
        });
    }

    @Override
    protected void onClickCallback(View v) {
        switch (v.getId()){
            case R.id.ivPianoTrainMode:
                PianoTrainActivity.pianoTrainMode=pianoTrainMode == PianoSongPlayer.Mode.APPRECIATE ? PianoSongPlayer.Mode.PRACTICE : PianoSongPlayer.Mode.APPRECIATE;
                startActivity(PianoTrainActivity.class);
                finish();
                break;
            case R.id.ivPianoTrainAdd:
                changeKeyCount(true);
                break;
            case R.id.ivPianoTrainReduce:
                changeKeyCount(false);
                break;
            case R.id.llPianoRecord:  // 点击开始录制
                if (mRecorder.isIdle()){
                    StkPermissionHelper.permission(Manifest.permission.RECORD_AUDIO)
                            .reqPermissionDesc(getString(R.string.get_record_permission))
                            .callback(new StkPermissionHelper.ACallback() {
                                @Override
                                public void onGranted() {
                                    mRecorder.start();
                                }
                            })
                            .request();
                }else {
                    if (!mRecorder.canStop()) {
                        ToastUtils.showShort(getString(R.string.piano_rec_time_too_short_tip_fmt, mRecorder.getMinRecDuration() / 1000));
                        return;
                    }
                    mRecorder.stop();
                    String fileName = FileUtils.getFileNameNoExtension(mRecorder.getRecFilePath());
                    String extension = FileUtils.getFileExtension(mRecorder.getRecFilePath());
                    RecordDialog recordDialog = new RecordDialog(this);
                    recordDialog.setListener(new RecordDialog.IListener() {
                        @Override
                        public void onClickConfirm(String newFileName) {
                            if (fileName.equals(newFileName)) {
                                recordDialog.dismiss();
                                return;
                            }
                            String newFilePath = mRecorder.getRecFolder() + File.separator + newFileName + "." + extension;
                            List<File> files = FileUtils.listFilesInDir(mRecorder.getRecFolder());
                            for (File file : files) {
                                if (file.getAbsolutePath().equals(newFilePath)) {
                                    ToastUtils.showShort(R.string.piano_file_name_exist_tip);
                                    return;
                                }
                            }
                            FileUtils.rename(mRecorder.getRecFilePath(), newFileName + "." + extension);
                            recordDialog.dismiss();
                            ToastUtils.showShort(R.string.record_success);
                        }
                    });
                    recordDialog.setCurrentFileName(fileName);
                    recordDialog.setType(Content.record_type_create);
                    recordDialog.show();
                    break;
                }
                break;
            case R.id.ivPianoRecordList://录音列表
                startActivity(RecordListActivity.class);
                break;
        }
    }

    @Override
    protected void initData() {
        mRecorder = AudioRecorder.getInstance();
        mRecorder.setRecFolderName(PianoConst.REC_FOLDER);
        //设置最小录制时长不能少于3秒
        mRecorder.setMinRecDuration(3 * 1000);
        mRecorder.addStateListener(this);

        createMidiProcessorAndPlay();

        today = TimeUtils.string2Millis(TimeUtils.getNowString(new SimpleDateFormat("yyyy.MM.dd")),new SimpleDateFormat("yyyy.MM.dd"));
        startTime = TimeUtils.getNowMills();
        saveRecord();
    }

    private void createMidiProcessorAndPlay() {
        if (mProcessor != null) {
            mProcessor.unregisterAllEventListeners();
        }
        mDataBinding.tvPianoTrainTitle.post(() -> {
            mDataBinding.tvPianoTrainTitle.setText(playFileBean.getFileName());
        });
        RxUtil.create(new RxUtil.Callback<Boolean>() {
            @Override
            public void doBackground(ObservableEmitter<Boolean> emitter) {
                boolean ret = false;
                try {
                    InputStream inputStream = null;
                    if (playFileBean.isAssetFile()) {
                        String assetFilePath = AssetUtil.getAssetFilePathFromAssetUri(playFileBean.getFileUriStr());
                        inputStream = getAssets().open(assetFilePath);
                    } else {
                        inputStream = getContentResolver().openInputStream(Uri.parse(playFileBean.getFileUriStr()));
                    }
                    MidiFile midiFile = new MidiFile(inputStream);
                    mDataBinding.pbProgress.post(() -> mDataBinding.pbProgress.setMax(midiFile.getNoteEvents().size()));
                    MidiProcessor processor = new MidiProcessor(midiFile);
                    mProcessor = processor;
                    processor.registerEventListener(new MidiEventListener() {
                        @Override
                        public void onStart(boolean fromBeginning) {
                            if (fromBeginning) {
                                mNoteEventCount = 0;
                                mDataBinding.pbProgress.setProgress(mNoteEventCount);
                            }
                        }

                        @Override
                        public void onEvent(MidiEvent event, long ms) {
                            if (event instanceof NoteOn) {
                                mNoteEventCount++;
                                mDataBinding.pbProgress.setProgress(mNoteEventCount);
                                NoteOn noteOn = (NoteOn) event;
                                mDataBinding.pianoSongPlayer.noteOn(noteOn.getNoteValue(), noteOn.getTick(), mNoteEventCount == 1);

                            } else if (event instanceof NoteOff) {
                                mNoteEventCount++;
                                mDataBinding.pbProgress.setProgress(mNoteEventCount);
                                NoteOff noteOff = (NoteOff) event;
                                mDataBinding.pianoSongPlayer.noteOff(noteOff.getNoteValue(), noteOff.getTick());
                                if (mNoteEventCount >= mDataBinding.pbProgress.getMax()) {
                                    processor.stop();
                                    onNoteEventComplete();
                                }
                            }
                        }

                        @Override
                        public void onStop(boolean finished) {
                        }
                    }, MidiEvent.class);
                    ret = true;
                } catch (Exception e) {
                    e.printStackTrace();
                }
                emitter.onNext(ret);
            }

            @Override
            public void accept(Boolean ret) {
                if (ret) {
                    mProcessor.start();
                }
            }
        });
    }

    private void onNoteEventComplete() {
        ThreadUtils.runOnUiThreadDelayed(() -> {
            if (isFinishing() || isDestroyed()) {
                return;
            }
            if (mDataBinding.pianoSongPlayer.getMode() == PianoSongPlayer.Mode.APPRECIATE) {
                ToastUtils.showShort(getString(R.string.appreciate_tips));
            } else {
                ToastUtils.showShort(getString(R.string.score_name,mDataBinding.pianoSongPlayer.getScore()));
            }
        }, mDataBinding.pianoSongPlayer.getNoteFallDuration());
    }

    private void changeKeyCount(boolean add) {
        PianoKeyBoard pianoKeyBoard = mDataBinding.pianoSongPlayer.getPianoKeyBoard();
        if (pianoKeyBoard == null) {
            return;
        }

        int newKeyCount = pianoKeyBoard.getKeyCount() + (add ? 1 : -1);
        pianoKeyBoard.setKeyCount(newKeyCount);
        mDataBinding.pianoSeekBar.setRatioKeyCountInScreen(pianoKeyBoard.getRatioKeyCountInScreen());
    }

    @Override
    public void onStateChanged(RecState state) {
        if (state == RecState.RECORDING) {
            mHandler.post(mTaskUpdateTime);
        } else {
            mHandler.removeCallbacks(mTaskUpdateTime);
            mDataBinding.tvPianoTrainTime.setText("00:00");
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mProcessor != null) {
            mProcessor.start();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mProcessor != null) {
            mProcessor.stop();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mRecorder.delStateListener(this);
        mRecorder.release();
        mHandler.removeCallbacks(mTaskUpdateTime);
        endTime = TimeUtils.getNowMills();
        PracticeTime practiceTime;
        List<PracticeTime> practiceTimeList = PracticeDatabase.getInstance(PianoTrainActivity.this).getPracticeDao().getTodayPracticeTime(today);
        if (practiceTimeList.size()==0) {
            practiceTime = new PracticeTime(today, endTime - startTime, 1);
            PracticeDatabase.getInstance(mContext).getPracticeDao().insertPracticeTime(practiceTime);
        }else {
            practiceTime=practiceTimeList.get(0);
            practiceTime.setCount(practiceTime.getCount()+1);
            practiceTime.setDuration(practiceTime.getDuration()+endTime-startTime);
            PracticeDatabase.getInstance(mContext).getPracticeDao().updatePracticeTime(practiceTime);
        }
    }

    private void saveRecord() {
        List<String> listSave = new ArrayList<>();
        String date=TimeUtils.getNowString(new SimpleDateFormat("yyyy.MM.dd"));
        listSave.add(date);
        List<String> listSp = AppSpUtils.getPracticeList();
        if (listSp != null && listSp.size() > 0) {
            if (!listSp.contains(date)){
                listSp.add(date);
            }
            AppSpUtils.savePracticeList(listSp);
        } else {
            AppSpUtils.savePracticeList(listSave);
        }
    }

}